#include <GL/glew.h>
#include <GL/freeglut.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <cstdio>
#include <common/common.hpp>
#include "mallet.hpp"
#include "table.hpp"
#include "puck.hpp"
#include "color_shader.hpp"
#include "texture_shader.hpp"

color_shader _colorshader;
texture_shader _texshader;
GLuint _texid;
mallet _mallet(0.08f, 0.15f, 32);
table _table;
puck _puck(0.06f, 0.02, 32);
glm::mat4 _view_projection_mat;

static GLboolean
init() {
	GLenum err = glewInit();
	if (err != GLEW_OK) {
		logv("fail to init glew");
		return GL_FALSE;
	}

	if (!GLEW_VERSION_3_3) {
		logv("not support for ogl3.3");
		return GL_FALSE;
	}
	logv("support for ogl3.3");

	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

	_texid = texture_util::loadtexture("./res/air_hockey_surface.png");
	if (_texid == 0) {
		logv("fail to load texture file");
		return GL_FALSE;
	}

    if (!_colorshader.init("./res/simple_vertex_shader.glsl", "./res/simple_fragment_shader.glsl")) {
    	logv("fail to init color shader");
    	return GL_FALSE;
    }

    if (!_texshader.init("./res/texture_vertex_shader.glsl", "./res/texture_fragment_shader.glsl")) {
    	logv("fail to init texture shader");
    	return GL_FALSE;
    }

    logv("init finish successfully");
    return GL_TRUE;
}

static void
position_table_inscene(glm::mat4 &modelmat) {
	modelmat = glm::mat4();
	modelmat = glm::rotate(modelmat, glm::radians(-90.0f), glm::vec3(1.0f, 0.0f, 0.0f));
	modelmat = _view_projection_mat * modelmat;
}

static void
position_object_inscene(GLfloat x, GLfloat y, GLfloat z, glm::mat4 &modelmat) {
	modelmat = glm::mat4();
	modelmat = glm::translate(modelmat, glm::vec3(x, y, z));
	modelmat = _view_projection_mat * modelmat;
}

static void
cb_display() {
	logv("cb_display");

   	glClear(GL_COLOR_BUFFER_BIT);

   	glm::mat4 modelmat;
   	position_table_inscene(modelmat);
   	_texshader.useprogram();
   	_texshader.setuniform(modelmat, _texid);
   	_table.binddata();
   	_table.draw();

   	position_object_inscene(0.0f, _mallet.getheight() / 2.0f, -0.4f, modelmat);
   	_colorshader.useprogram();
   	_colorshader.setuniform(modelmat, 1.0f, 0.0f, 0.0f);
   	_mallet.binddata();
   	_mallet.draw();

	position_object_inscene(0.0f, _mallet.getheight() / 2.0f, 0.4f, modelmat);
	_colorshader.setuniform(modelmat, 0.0f, 0.0f, 1.0f);
   	_mallet.draw();   	

   	position_object_inscene(0.0f, _puck.getheight() / 2.0f, 0.0f, modelmat);
   	_colorshader.setuniform(modelmat, 0.8f, 0.8f, 1.0f);
   	_puck.binddata();
   	_puck.draw();

	glFlush();
}

static void 
cb_reshape(int width, int height) {
	logv("cb_reshape: width:%d, height:%d", width, height);
	glViewport (0, 0, width, height);

	GLfloat aspect = (GLfloat)width / (GLfloat)height;
	glm::mat4x4 persmat = glm::perspective(glm::radians(45.0f), aspect, 1.0f, 10.0f);
	glm::mat4x4 viewmat = glm::lookAt(glm::vec3(0.0f, 1.2f, 2.2f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
	_view_projection_mat = persmat * viewmat;
}

static void
cb_close() {
	logv("cb_close");
	_colorshader.deleteprogram();
	_texshader.deleteprogram();
}

int
main(int argc, char *argv[]) {
	glutInit(&argc, argv);
	glutInitWindowSize(800, 600);
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
	glutInitWindowPosition(0, 0);
	
	glutCreateWindow("AirHockey - Improved");
	
	if (init() == GL_FALSE)
		return 0;

	glutDisplayFunc(cb_display);
	glutReshapeFunc(cb_reshape);
	glutCloseFunc(cb_close);

	glutMainLoop();
	return 0;
}
